package com.fans.admin.quartz.job;

import java.util.Date;

import org.quartz.DisallowConcurrentExecution;
import org.quartz.Job;
import org.quartz.JobDataMap;
import org.quartz.JobDetail;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.quartz.JobKey;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.transaction.annotation.Transactional;

import com.fans.admin.quartz.constant.QtzConstant;
import com.fans.admin.quartz.constant.QtzConstant.ScheduleStatus;
import com.fans.admin.quartz.constant.QtzConstant.ScheduleStatusEnum;
import com.fans.admin.quartz.entity.ScheduleJob;
import com.fans.admin.quartz.entity.ScheduleLog;
import com.fans.admin.quartz.service.IScheduleJobService;
import com.fans.admin.quartz.service.IScheduleLogService;
import com.fans.admin.quartz.util.ExceptionUtil;
import com.fans.common.utils.BlankUtils;
import com.fans.common.utils.ReflectUtil;
import com.fans.common.utils.SpringUtil;

/**
 * 任务工厂
 * 
 * @DisallowConcurrentExecution 禁止并发执行多个相同定义的JobDetail, 这个注解是加在Job类上的
 * @author fanhoahao
 *
 */
@DisallowConcurrentExecution
public class QuartzJobFactory implements Job {
	private Logger logger = LoggerFactory.getLogger(QuartzJobFactory.class);
	
	
	/**
	 * 获取 scheduleJobService
	 * 
	 * @return IScheduleJobService scheduleJobService
	 */
	public IScheduleJobService getScheduleJobService() {
		return SpringUtil.getBean(IScheduleJobService.class);
	}

	/**
	 * 获取 scheduleLogService
	 * 
	 * @return IScheduleLogService scheduleLogService
	 */
	public IScheduleLogService getScheduleLogService() {
		return SpringUtil.getBean(IScheduleLogService.class);
	}

	/**
	 * 执行任务中的方法
	 * @param job
	 * @param jobDataMap
	 * @throws Exception
	 */
	@Transactional
	public Object execute(ScheduleJob job, JobDataMap jobDataMap) throws Exception {
		/*
		 * String paramKey = job.getJobGroup() + "." + job.getJobName();
		 * 
		 * QuartzScheduleJobEntity paramJob = null; try{ paramJob =
		 * (QuartzScheduleJobEntity)jobDataMap.get(paramKey); } catch(Exception
		 * e) { paramJob = null; }
		 */
		
		//判断jobClass是springBean还是class
		if (job.jobClassIsBeanNameOrNot()) {// 是spring bean
			Object object = SpringUtil.getBean(job.getJobClass());
			Object invokeMethod = ReflectUtil.invokeMethod(object, job.getJobMethod(), job);
			return invokeMethod;
		} else {
			String jobClass = job.getJobClass();
			@SuppressWarnings("unchecked")
			Class<? extends QuartzJob> clazz = (Class<? extends QuartzJob>) Class.forName(jobClass);
			Object invokeMethod = ReflectUtil.invokeMethod(clazz.newInstance(), job.getJobMethod(), job);
			return invokeMethod;
		}
	}
	
	/**
	 * 执行任务
	 */
	@Override
	public void execute(JobExecutionContext context) {
		// TODO Auto-generated method stub
		JobDetail jobDetail = context.getJobDetail();
		
		JobKey key = jobDetail.getKey();
		String jobId =  key.getName();
		ScheduleJob job = getScheduleJobService().selectById(jobId);
		logger.info("======================================开始任务:" + job.getJobName() + "========================================");
		if(null == job) {
			logger.error("执行任务，任务ID【" + jobId + "】失败，没有找到相应ScheduleJob Entity信息。");
			return;
		}
		
		ScheduleLog log = new ScheduleLog();
		log.setScheduleJobId(job.getId());
		Object execute = null;
		try {
			long start = System.currentTimeMillis();
			preExecute(context, job, log);
			execute = execute(job, jobDetail.getJobDataMap());
			job.setJobUsedTime(System.currentTimeMillis() - start);
		} catch (Exception e) {
			logger.error("执行任务【" + job.getJobName() + "】失败，原因：\n" + ExceptionUtil.getStackTraceAsString(e));
			dealException(job, log, e);
		} finally {
			if ("-1".equals(String.valueOf(execute))) {
				log.setExceptionStackTrace("定时器bean代码异常，具体原因请查看后台打印日志！");
				log.setStatus(QtzConstant.ScheduleLogStatusEnum.FAIL.getCode());
				job.setJobExceptionCount(job.getJobExceptionCount() + 1);
				// job.setJobStatus(ScheduleJobStatusEnum.EXCEPTION.getCode());
				job.setLastExceptionTime(new Date());
			}
			log.setEndTime(new Date());
			log.preInsert();
			getScheduleLogService().insert(log);
			getScheduleJobService().updateById(job);
		}
		logger.info("======================================结束任务:" + job.getJobName() + "========================================");
	}

	// 任务执行前操作
	public void preExecute(JobExecutionContext context, ScheduleJob job, ScheduleLog log)
			throws JobExecutionException {
		if (job == null) {
			throw new JobExecutionException("执行任务【" + context.getJobDetail().getKey() + "】失败，该任务不存在。");
		}
		job.setStatus(ScheduleStatus.NORMAL.getValue());
		job.setJobStatus(ScheduleStatusEnum.USABLE.getCode());
		job.setLastExecTime(new Date());
		job.setNextExecTime(context.getNextFireTime());
		job.preUpdate();
		if(BlankUtils.isNotBlank(job.getJobExecCount())){
			job.setJobExecCount(job.getJobExecCount() + 1);
		}else{
			job.setJobExecCount((long)1);
		}
		if(BlankUtils.isBlank(job.getLastExecTime())){
			job.setLastExecTime(new Date());
		}
		log.setStatus(QtzConstant.ScheduleLogStatusEnum.SUCCESS.getCode());
		log.setExecTime(job.getLastExecTime());
	}
	
	// 异常处理
	private void dealException(ScheduleJob job, ScheduleLog log, Exception e) {
		log.setStatus(QtzConstant.ScheduleLogStatusEnum.FAIL.getCode());
		log.setExceptionStackTrace(ExceptionUtil.getStackTraceAsString(e));
		job.setJobExceptionCount(job.getJobExceptionCount() + 1);
		// job.setJobStatus(ScheduleJobStatusEnum.EXCEPTION.getCode());
		job.setLastExceptionTime(new Date());
	}
	/*
	private void test(JobKey key) throws IOException, InterruptedException {
		boolean isContione = true;
		int minutes = 0;
		System.out.println("开始：：**" + key.getGroup() + "." + key.getName() + "*****************************************");
		while (isContione) {
			Thread.sleep(1000);
			minutes++;
			System.out.println(minutes + "=========" + key.getGroup() + "." + key.getName() + "==============");
			ClassPathResource cpr = new ClassPathResource("");
			System.out.println("**" + cpr.getFile().getPath() + "*****************************************");
			if(minutes >= 10) {
				isContione = false;
				System.out.println("结束：：**" + key.getGroup() + "." + key.getName() + "*****************************************");
			}
		}
	}*/
}
